﻿using Hippo.Common.Extensions;

namespace Hippo.Common
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Text.RegularExpressions;

    /// <summary>
    /// Specifies all the available command line options
    /// </summary>
    public class OptionSet : List<Option>
    {
        /// <summary>
        /// Adds the specified key.
        /// </summary>
        /// <param name="label">Documentation label</param>
        /// <param name="description">Text that describes what this command line argument means.</param>
        /// <param name="expression">Regular expression that will match the command line argument.</param>
        /// <param name="action">The action that should be executed with the value of this command line argument.</param>
        public void Add(string label, string description, string expression, Action<string> action)
        {
            Add(new Option(label, description, expression, action));
        }

        /// <summary>
        /// Returns a <see cref="System.String"/> that represents this instance.
        /// </summary>
        /// <returns>
        /// A <see cref="System.String"/> that represents this instance.
        /// </returns>
        public override string ToString()
        {
            const int MaxLengthDescription = 54;
            var result = new StringBuilder();
            foreach (var option in this)
            {
                string label = option.Label;
                foreach (var row in option.Description.SplitOnWholeWords(MaxLengthDescription))
                {
                    result.AppendFormat("{0,-24}{1}\n", label, row);
                    label = string.Empty;
                }
            }

            return result.ToString();
        }

        /// <summary>
        /// Parses the specified arguments
        /// </summary>
        /// <param name="args">Command line arguments that should be parseds</param>
        public void Parse(string[] args, out List<string> unrecognizedArguments)
        {
            unrecognizedArguments = new List<string>();

            try
            {
                for (var i = 0; i < args.Length; i++)
                {
                    var option = this.FirstOrDefault(o => Regex.IsMatch(args[i], o.Expression, RegexOptions.IgnoreCase));

                    /* Unrecognized option */
                    if (option == null)
                    {
                        unrecognizedArguments.Add(args[i]);
                        continue;
                    }

                    /* Expecting a value */
                    if (option.Expression.Contains("<value>"))
                    {
                        var valueGroup = Regex.Match(args[i], option.Expression, RegexOptions.IgnoreCase).Groups["value"];
                        if (valueGroup != null && !string.IsNullOrEmpty(valueGroup.Value))
                        {
                            option.Action(valueGroup.Value);
                        }
                        else
                        {
                            option.Action(args[++i]);
                        }
                    }
                    else
                    {
                        option.Action(null);
                    }
                }
            }
            catch (Exception e)
            {
                throw new OptionsException("Failed to parse the command line: " + e.Message);
            }
        }
    }
}
